#ifndef NCSPROPTREE_P_H
#define NCSPROPTREE_P_H

#include "ncsproptree.h"
#include "ncspropmgr_p.h"
#include <QItemDelegate>
#include <QApplication>
#include <QPainter>
#include <QFocusEvent>
#include <QHeaderView>

class NcsProp; 
class NcsPropEditorDelegate ;

class NcsPropTreePrivate
{
    NcsPropTree *q = nullptr;

public:

    NcsPropTreePrivate(NcsPropTree* _this) : q(_this)
    { }

    void init(QWidget *parent);

    void setItemProp(QTreeWidgetItem* item, NcsProp* p) { m_item2prop[item] = p; }
    NcsProp* itemProp(QTreeWidgetItem* item) const
    {
        auto it = m_item2prop.constFind(item);
        if (it != m_item2prop.constEnd())
            return it.value();
        return NULL;
    }

    void deleteItemProp(NcsProp* prop)
    {
        if (!prop) return; 
        QTreeWidgetItem* item = q->itemFromProperty(prop);
        if (!item) return;
        q->m_prop2item.remove(prop);
        m_item2prop.remove(item);
        m_itembgcolor.remove(item);
        delete item;
    }

    QTreeWidgetItem* indexToItem(const QModelIndex &index) const { return q->itemFromIndex(index); }

    QTreeWidget* treeWidget() const { return q; }

    NcsProp* indexToProperty(const QModelIndex &index) const
    {
        QTreeWidgetItem *item = this->indexToItem(index);
        return itemProp(item);
    }

    //! return true if {column} is the last column of qtreewidget. 
    bool lastColumn(int column) const
    {
        return q->header()->visualIndex(column) == q->columnCount() - 1;
    }


    /*
    void disableItem(QTreeWidgetItem *item) const;
    void enableItem(QTreeWidgetItem *item) const;
    */
    bool hasValue(QTreeWidgetItem *item) const;

    QColor calculatedBackgroundColor(QTreeWidgetItem* item) const;

	void setBackgroundColor(QTreeWidgetItem* item, const QColor &color)
	{
		if (item->treeWidget() != q) 
			return;
		if (color.isValid())
			m_itembgcolor[item] = color;
		else
			m_itembgcolor.remove(item);
		q->viewport()->update();
	}



    bool markNullProp() const { return m_markNullProp; }

    //void editItem(QtBrowserItem *browserItem);
    //QTreeWidgetItem *editedItem() const;

    //void updateItem(QTreeWidgetItem *item);

    QWidget* createEditor(NcsProp* prop, QWidget* parent) 
    { 
        Q_ASSERT(q->m_propmgr == prop->propMgr());
        NcsPropMgrPrivate* d = prop->propMgr()->d; 

        // then search prop_factory . 
        if (d->prop_factory.contains(prop))
        {
            auto fn = d->prop_factory[prop];
            QWidget* editor = fn(prop);
			editor->setParent(parent);
			editor->setAutoFillBackground(true);
            return editor; 
        }

        int ptype = prop->propType();

        if (d->type_factory.contains(ptype))
        {
            auto fn = d->type_factory[ptype];
            QWidget* editor = fn(prop);
			editor->setParent(parent);
			editor->setAutoFillBackground(true);
            return editor; 
        }

        return nullptr; 
    }

    /* Create a persistant widget as item's widget at 2nd column. 
     * Property's displaying and editing are all integrated into this widget. 
     */
    QWidget* createItemWidget(NcsProp* prop, QWidget* parent) 
    { 
        return nullptr; 

        // search widget creator for property. 
        if (m_itemwidget_factory_for_prop.contains(prop))
        {
            auto fn = m_itemwidget_factory_for_prop[prop];
            QWidget* editor = fn(prop);
			editor->setParent(parent);
			editor->setAutoFillBackground(true);
            return editor; 
        }

        register int ptype = prop->propType();

        if (m_itemwidget_factory_for_type.contains(ptype))
        {
            auto fn = m_itemwidget_factory_for_type[ptype];
            QWidget* editor = fn(prop);
			editor->setParent(parent);
			editor->setAutoFillBackground(true);
            return editor; 
        }

        return nullptr; 
    }

    QTreeWidgetItem* editedItem() const; 

    void resetState(uint32_t clear_options)
    {
        m_item2prop.clear();
        m_itembgcolor.clear();
        m_headerVisible = true;
        //m_delegate  should kept. 
        m_markNullProp = true;
        m_browserChangedBlocked = false;

        if (clear_options & NcsPropTree::CLEAR_FACTORY)
        {
            m_itemwidget_factory_for_prop.clear();
            m_itemwidget_factory_for_type.clear();
        }
    }

public:

    QMap<QTreeWidgetItem*, NcsProp*> m_item2prop; 
    QMap<QTreeWidgetItem*, QColor> m_itembgcolor;

    QMap<NcsProp*, NcsPropMgr::EditorFn>    m_itemwidget_factory_for_prop; //某个属性独有的编辑器工厂函数.  次高优先级
    QMap<int,      NcsPropMgr::EditorFn>    m_itemwidget_factory_for_type; // 每个类型的编辑器工厂函数.  普通优先级.

    bool m_headerVisible = true;
    NcsPropEditorDelegate *m_delegate = nullptr;
    bool m_markNullProp = true;
    bool m_browserChangedBlocked = false;
    QIcon m_expandIcon;

}; // NcsPropTreePrivate


// Draw an icon indicating opened/closing branches
static QIcon drawIndicatorIcon(const QPalette &palette, QStyle *style)
{
    QPixmap pix(14, 14);
    pix.fill(Qt::transparent);
    QStyleOption branchOption;
    QRect r(QPoint(0, 0), pix.size());
    branchOption.rect = QRect(2, 2, 9, 9); // ### hardcoded in qcommonstyle.cpp
    branchOption.palette = palette;
    branchOption.state = QStyle::State_Children;

    QPainter p;
    // Draw closed state
    p.begin(&pix);
    style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
    p.end();
    QIcon rc = pix;
    rc.addPixmap(pix, QIcon::Selected, QIcon::Off);
    // Draw opened state
    branchOption.state |= QStyle::State_Open;
    pix.fill(Qt::transparent);
    p.begin(&pix);
    style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
    p.end();

    rc.addPixmap(pix, QIcon::Normal, QIcon::On);
    rc.addPixmap(pix, QIcon::Selected, QIcon::On);
    return rc;
}
/*
void NcsPropTreePrivate::disableItem(QTreeWidgetItem *item) const
{
    Qt::ItemFlags flags = item->flags();
    if (flags & Qt::ItemIsEnabled) {
        flags &= ~Qt::ItemIsEnabled;
        item->setFlags(flags);
        m_delegate->closeEditor(m_itemToIndex[item]->property());
        const int childCount = item->childCount();
        for (int i = 0; i < childCount; i++) {
            QTreeWidgetItem *child = item->child(i);
            disableItem(child);
        }
    }
}
*/

/*
void NcsPropTreePrivate::enableItem(QTreeWidgetItem *item) const
{
    Qt::ItemFlags flags = item->flags();
    flags |= Qt::ItemIsEnabled;
    item->setFlags(flags);
    const int childCount = item->childCount();
    for (int i = 0; i < childCount; i++) {
        QTreeWidgetItem *child = item->child(i);
        NcsProp *property = m_itemToIndex[child]->property();
        if (property->isEnabled()) {
            enableItem(child);
        }
    }
}
*/

/*
void NcsPropTreePrivate::propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex)
{
    QTreeWidgetItem *afterItem = m_indexToItem.value(afterIndex);
    QTreeWidgetItem *parentItem = m_indexToItem.value(index->parent());

    QTreeWidgetItem *newItem = 0;
    if (parentItem) {
        newItem = new QTreeWidgetItem(parentItem, afterItem);
    }
    else {
        newItem = new QTreeWidgetItem(m_treeWidget, afterItem);
    }
    m_itemToIndex[newItem] = index;
    m_indexToItem[index] = newItem;

    newItem->setFlags(newItem->flags() | Qt::ItemIsEditable);
    m_treeWidget->setItemExpanded(newItem, true);

    updateItem(newItem);
}
*/


/*
void NcsPropTreePrivate::updateItem(QTreeWidgetItem *item)
{
    NcsProp *property = m_itemToIndex[item]->property();
    QIcon expandIcon;
    if (property->hasValue()) {
        const QString valueToolTip = property->valueToolTip();
        const QString valueText = property->valueText();
        item->setToolTip(1, valueToolTip.isEmpty() ? valueText : valueToolTip);
        item->setIcon(1, property->valueIcon());
        item->setText(1, valueText);
    }
    else if (markNullProp() && !m_treeWidget->rootIsDecorated()) {
        expandIcon = m_expandIcon;
    }
    item->setIcon(0, expandIcon);
    item->setFirstColumnSpanned(!property->hasValue());
    const QString descriptionToolTip = property->descriptionToolTip();
    const QString propertyName = property->propertyName();
    item->setToolTip(0, descriptionToolTip.isEmpty() ? propertyName : descriptionToolTip);
    item->setStatusTip(0, property->statusTip());
    item->setWhatsThis(0, property->whatsThis());
    item->setText(0, propertyName);
    bool wasEnabled = item->flags() & Qt::ItemIsEnabled;
    bool isEnabled = wasEnabled;
    if (property->isEnabled()) {
        QTreeWidgetItem *parent = item->parent();
        if (!parent || (parent->flags() & Qt::ItemIsEnabled))
            isEnabled = true;
        else
            isEnabled = false;
    }
    else {
        isEnabled = false;
    }
    if (wasEnabled != isEnabled) {
        if (isEnabled)
            enableItem(item);
        else
            disableItem(item);
    }
    m_treeWidget->viewport()->update();
}
*/

inline QColor NcsPropTreePrivate::calculatedBackgroundColor(QTreeWidgetItem* item) const
{
    QTreeWidgetItem*i = item;
    const auto itEnd = m_itembgcolor.constEnd();
    while (i) {
        auto it = m_itembgcolor.constFind(i);
        if (it != itEnd)
            return it.value();
        i = i->parent();
    }
    return QColor();
}

/*
void NcsPropTreePrivate::editItem(QtBrowserItem *browserItem)
{
    if (QTreeWidgetItem *treeItem = m_indexToItem.value(browserItem, 0)) {
        m_treeWidget->setCurrentItem(treeItem, 1);
        m_treeWidget->editItem(treeItem, 1);
    }
}
*/

class NcsPropEditorDelegate : public QItemDelegate
{
    Q_OBJECT

public:
    NcsPropEditorDelegate(NcsPropTreePrivate* treeprivate, QObject *parent = 0)
        : QItemDelegate(parent), m_treepriv(treeprivate), m_editedItem(0), m_editedWidget(0)
        {}

    virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
            const QModelIndex &index) const override;

    virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
            const QModelIndex &index) const override;

    virtual void paint(QPainter *painter, const QStyleOptionViewItem &option,
            const QModelIndex &index) const override;

    virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;

	//! Disable default setModelData() function.
    void setModelData(QWidget *, QAbstractItemModel *, const QModelIndex &) const {}

    void setEditorData(QWidget *, const QModelIndex &) const {}

    virtual bool eventFilter(QObject *object, QEvent *event) override;

    void closeEditor(NcsProp *prop);

    QTreeWidgetItem *editedItem() const { return m_editedItem; }

private slots:
    void slotEditorDestroyed(QObject *object);

private:
    int indentation(const QModelIndex &index) const;

    mutable QMap<QWidget*, NcsProp*> m_edit2prop;
    mutable QMap<NcsProp*, QWidget*> m_prop2edit;
    
    NcsPropTreePrivate *m_treepriv; // access tree data. 
    mutable QTreeWidgetItem *m_editedItem;
    mutable QWidget *m_editedWidget;
};


#endif // NCSPROPTREE_P_H
